namespace Microsoft.ServiceModel.Samples.Discovery.Protocol
{
    using System;
    using System.Text;
    using System.Collections.Generic;
    using System.ServiceModel.Channels;

    public class CompositeTransportBindingElement<TChannelBinding, TListenerBinding>
        : TransportBindingElement
        where TChannelBinding : Binding, new()
        where TListenerBinding : Binding, new()
    {
        TChannelBinding channelBinding;
        TListenerBinding listenerBinding;

        public CompositeTransportBindingElement(TChannelBinding channelBinding, 
            TListenerBinding listenerBinding)
        {
            this.channelBinding = channelBinding;
            this.listenerBinding = listenerBinding;
        }

        public CompositeTransportBindingElement(
            CompositeTransportBindingElement<TChannelBinding, TListenerBinding>
            other)
            : base(other)
        {
            this.channelBinding = (TChannelBinding)other.channelBinding;
            this.listenerBinding = (TListenerBinding)other.listenerBinding;
        }

        public TChannelBinding ChannelBinding
        {
            get { return this.channelBinding; }
        }

        public TListenerBinding ListenerBinding
        {
            get { return this.listenerBinding; }
        }

        public override bool CanBuildChannelFactory<TChannel>(
            BindingContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            return channelBinding.CanBuildChannelFactory<TChannel>(
                context.BindingParameters);
        }

        public override bool CanBuildChannelListener<TChannel>(
            BindingContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            return listenerBinding.CanBuildChannelListener<TChannel>(
                context.BindingParameters);
        }

        public override IChannelFactory<TChannel> BuildChannelFactory<TChannel>(
            BindingContext context)
        {
            return channelBinding.BuildChannelFactory<TChannel>(
                context.BindingParameters);
        }

        public override IChannelListener<TChannel> BuildChannelListener<TChannel>(BindingContext context)
        {
            return listenerBinding.BuildChannelListener<TChannel>(
                context.ListenUriBaseAddress,
                context.ListenUriRelativeAddress,
                context.ListenUriMode,
                context.BindingParameters);
        }

        public override BindingElement Clone()
        {
            return new CompositeTransportBindingElement<TChannelBinding, TListenerBinding>(this);
        }

        public override T GetProperty<T>(BindingContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            T result = this.channelBinding.GetProperty<T>(context.BindingParameters);
            if (result != default(T))
            {
                return result;
            }

            result = this.listenerBinding.GetProperty<T>(context.BindingParameters);
            if (result != default(T))
            {
                return result;
            }

            return context.GetInnerProperty<T>();
        }

        public override string Scheme
        {
            get 
            { 
                // The binding scheme is checked against the listen URI scheme 
                // hence return the scheme of the listener binding.
                return listenerBinding.Scheme; 
            }
        }
    }
}
